///////////////////////////////////////////////////////////////////////////////
//
// KeepAlive.cxx
// Main file for KeepAlive application. The keep-alive application attempts to
// ensure that a given executable file keeps running if it somehow crashes or
// is closed.
//
// Although intended for use with the Kiosk fountain GUI application, this can
// really be used with anything - just pass it the executable name as an arg.
//
// 12 December 2011
// Alex Kersten
//
////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <Windows.h>

//Debug flag - set to false when deploying!
//Set to true if you want a console with output - I don't know if that's
//a great idea since if someone manages to catch sight of the console they
//can stop the whole thing by hitting X, but it shoudl be useful for debugging.
//I'm just going to set it to false, since I'm pretty sure it works fine.
const bool CONSOLE_DEBUG = false;

//Cooldown time after synchronous lock breaks until we restart the program
const unsigned long SLEEP_TIME = 500;

using namespace std;

int _tmain(int argc, TCHAR** argv) {
	//First we'll hide the console window because we don't want people to
	//mess around with it
	if (!CONSOLE_DEBUG) {
		HWND consoleHandle = GetConsoleWindow();

		SetWindowPos(consoleHandle,
			NULL, 0, 0, 0, 0, SWP_HIDEWINDOW| SWP_NOMOVE);
	}
	
	//Check our arg count, it should be 1 which should be the name of the
	//program to keep alive; remember that argc = 1 + number of arguments,
	//since the first argument is always the name of this program.
	if (argc < 2) {
		MessageBox(NULL,
			L"Usage: KeepAlive <name of executable> <arguments>",
			L"KeepAlive - Error", NULL);

		return 1;
	}

	//Read in argument to a string
	wstring programName(argv[1]);

	//Check that this file actually exists...
	ifstream checkStream;
	checkStream.open(programName, ios::binary);

	if (!checkStream.good()) {
		MessageBox(NULL,
			L"Could not access file specified.", L"KeepAlive - Error", NULL);

		return 1;
	}
	checkStream.close();

	wstring *programArgsArray;

	if (argc > 2) {
		//Get program arguments into an array
		programArgsArray = new wstring[argc - 2];

		for (int i = 2; i < argc; i++) {
			programArgsArray[i - 2] = wstring(argv[i]);
			programArgsArray[i - 2] += L" ";
		}
	}

	//Print some debug information
	if (CONSOLE_DEBUG) {
		cout << "Program name: ";
		wcout << programName.c_str();
		cout << endl;

		cout << "Arguments:" << endl;
		for (int i = 0; i < argc - 2; i++) {
			cout << " ";
			wcout << programArgsArray[i].c_str();
			cout << endl;
		}
		cout << endl;

		cout << "Heartbeat interval: " << SLEEP_TIME << endl << endl;

		cout << "Starting in 3 seconds..." << endl << endl;
		Sleep(3000);
	}

	//Windows wants one big command line with all the args, so make it happy
	//We can just shove the program name in here too
	wstring programCmdLine(programName);
	programCmdLine += L" ";
	for (int i = 0; i < argc - 2; i++) {
		programCmdLine += programArgsArray[i];
	}

	if (argc > 2) {
		delete[] programArgsArray;
	}

	//Callback information stored here
	PROCESS_INFORMATION processInfo;

	//Required by CreateProcess
	STARTUPINFO processStartup;

	//Start with a clean slate for the refbacks
	ZeroMemory(&processInfo, sizeof(processInfo));
	ZeroMemory(&processStartup, sizeof(processStartup));

	//Main keep-alive loop
	while (true) {
		//(Re)start the process.
		if (!CreateProcess(NULL, &programCmdLine[0],
				NULL,
				NULL,
				false,
				CREATE_NEW_CONSOLE,
				NULL,
				NULL,
				&processStartup,
				&processInfo)
		) {
			MessageBox(NULL, L"Failed to execute!", L"KeepAlive - Error", NULL);
			break;
		} else {
			//Synchronously lock on the process handle
			WaitForSingleObject(processInfo.hProcess, INFINITE);

			//When we lose the lock (i.e. the process exits, forget we had it)
			CloseHandle(processInfo.hProcess);
			CloseHandle(processInfo.hThread);
		}

		if (CONSOLE_DEBUG) {
			cout << "Restarted ";
			wcout << programName.c_str();
			cout << " with PID ";
			cout << processInfo.dwProcessId << endl;
		}
		
		//Wait for a while
		Sleep(SLEEP_TIME);
	}

	return 0;
}
